Skip to content

[model] feat: add MiniMax-M2 MoE bridge - #2602

Merged
yaoyu-33 merged 45 commits into
mainfrom
yuya/add-minimax-m2-bridge
Mar 23, 2026
Merged

[model] feat: add MiniMax-M2 MoE bridge#2602
yaoyu-33 merged 45 commits into
mainfrom
yuya/add-minimax-m2-bridge

Conversation

@yaoyu-33

@yaoyu-33 yaoyu-33 commented Mar 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Add Megatron Bridge for MiniMaxAI/MiniMax-M2, a sparse MoE model with 256 experts, top-8 sigmoid routing, and expert bias correction.

Bridge (minimax_m2_bridge.py)

  • Config mapping with manual rotary_percent calculation (rotary_dim / head_dim)
  • Per-expert weight mapping using block_sparse_moe prefix (w1/w2/w3 format)
  • Sigmoid routing with e_score_correction_bias buffer mapping
  • QK layernorm intentionally disabled (full-dim vs per-head mismatch — documented TODO)
  • MoE settings: grouped gemm, alltoall dispatcher, aux_loss load balancing

Examples

  • conversion.sh — single/multi-GPU round-trip and checkpoint import/export
  • inference.sh — text generation with TP support
  • verify_toy_model.py — creates a toy model and runs forward-pass comparison via compare.py

Tests

  • test_minimax_m2_conversion.py — toy model creation, single-GPU round-trip, TP=2 and PP=2 parallelism tests

Bug fix in compare.py

  • Truncate Megatron logits to HF vocab size before comparison, fixing shape mismatch when Megatron pads vocab for GPU kernel efficiency

Verification

  • 1-GPU forward pass: cosine similarity 0.999990 ✅
  • EP=2 forward pass: cosine similarity 0.999990 ✅
  • EP=2 weight round-trip: all 43 weights match ✅
  • TP=2+EP=2 weight round-trip: all weights match ✅

Known Limitations

  • QK layernorm weights (q_norm, k_norm) are dropped — MiniMax-M2 uses full-dimension QK norm while Megatron uses per-head. Acceptable for fine-tuning.
  • MTP (Multi-Token Prediction) modules are not mapped.

Test plan

  • Run verify_toy_model.py on 1-GPU, TP=2, EP=2
  • Run test_minimax_m2_conversion.py with 2+ GPUs
  • Verify weight round-trip with TP=2+EP=2

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Added MiniMax-M2 model support with conversion and inference capabilities.
    • Added toy model verification script for MiniMax-M2 conversion testing.
  • Bug Fixes

    • Improved Megatron/HF model comparison with proper logit truncation and similarity metrics.
  • Refactor

    • Consolidated deprecated model provider classes into unified base providers.
    • Migrated recipes to AutoBridge-based model configuration.
  • Tests

    • Updated tests to align with new provider infrastructure.
    • Added comprehensive MiniMax-M2 conversion test suite.
    • Removed deprecated provider test coverage.

@copy-pr-bot

copy-pr-bot Bot commented Mar 1, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Mar 1, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This PR adds MiniMax-M2 model support to Megatron-Bridge and consolidates the model provider architecture by removing numerous deprecated provider classes across DeepSeek, Llama, Nemotron, GLM, GPT-OSS, and Kimi variants, replacing them with unified bridge implementations and generic providers (GPTModelProvider, MLAModelProvider, MambaModelProvider). It includes new conversion/inference scripts, updates to recipes, tests, and examples.

Changes

Cohort / File(s) Summary
MiniMax-M2 Bridge & Support
src/megatron/bridge/models/minimax_m2/*, examples/models/minimax_m2/*, tests/functional_tests/models/minimax_m2/*
New MiniMax-M2 model bridge with provider configuration, mapping registry for MoE architecture; adds conversion/inference shell scripts and comprehensive conversion tests including toy model creation and multi-GPU verification.
Example Conversion Updates
examples/conversion/compare_hf_and_megatron/compare.py
Modified Megatron model comparison: disabled MTP layer configuration, added dtype normalization for NCCL broadcasting, removed model reloading, added logits truncation to HF vocabulary size, and enhanced comparison metrics (cosine similarity, threshold-based status).
Safe Repository Whitelist
src/megatron/bridge/models/hf_pretrained/utils.py
Added "MiniMaxAI" to SAFE_REPOS list for HF model loading.
Bridge Module Initialization
src/megatron/bridge/models/__init__.py
Consolidated public API exports: removed numerous DeepSeek, Llama, Nemotron, GPT-OSS provider classes; added MiniMaxM2Bridge, LlamaBridge, NemotronHBridge to unified exports.
Deprecated DeepSeek Provider Removal
src/megatron/bridge/models/deepseek/deepseek_provider.py, src/megatron/bridge/models/deepseek/__init__.py
Removed all DeepSeek provider classes (DeepSeekModelProvider, DeepSeekV2\*/DeepSeekV3ModelProvider, MoonlightModelProvider16B) and deprecation warnings; retained only bridge definitions.
Llama Provider Consolidation
src/megatron/bridge/models/llama/*
Deleted entire llama_provider.py file (20+ provider variants); updated __init__.py to expose only LlamaBridge; simplified public API surface.
Llama-Nemotron Provider Refactor
src/megatron/bridge/models/llama_nemotron/*
Replaced Llama-based provider inheritance with GPTModelProvider; removed per-model provider variants; updated bridge return type to LlamaNemotronHeterogeneousProvider.
GLM Provider & File Removal
src/megatron/bridge/models/glm/*, src/megatron/bridge/models/glm_vl/glm_45v_provider.py
Deleted glm45_provider.py (3 provider classes removed); updated GLM45VModelProvider to inherit from GPTModelProvider with expanded configuration fields; narrowed GLM module exports to GLM45Bridge.
GPT-OSS Provider Removal
src/megatron/bridge/models/gpt_oss/*
Completely removed gpt_oss_provider.py file (GPTOSSProvider base and variants); updated __init__.py to export only bridge.
Kimi Provider Consolidation
src/megatron/bridge/models/kimi/*
Deleted kimi_provider.py (KimiK2Provider removed); switched imports to use KimiBridge; updated recipes to use AutoBridge instead of direct provider instantiation.
Nemotron & NemotronH Provider Removal
src/megatron/bridge/models/nemotron/*, src/megatron/bridge/models/nemotronh/*
Deleted entire nemotron_provider.py and nemotron_h_provider.py (13+ provider classes removed); narrowed module exports to bridge definitions only.
Nemotron-VL Provider Refactor
src/megatron/bridge/models/nemotron_vl/*
Replaced NemotronNano12Bv2Provider inheritance with MambaModelProvider; expanded public configuration fields; removed legacy provider exports.
Recipe Updates (Kimi, Moonlight, Nemotron)
src/megatron/bridge/recipes/kimi/kimi_k2.py, src/megatron/bridge/recipes/moonlight/moonlight_16b.py, src/megatron/bridge/recipes/nemotronh/*
Replaced direct provider instantiation with AutoBridge/MambaModelProvider usage; updated imports and provider configurations across pretrain/SFT/PEFT paths; adjusted activation function references.
Example & Test Sampler Updates
examples/conversion/.., tests/functional_tests/data/test_samplers.py
Updated import statements to use new provider classes (GPTModelProvider, MLAModelProvider); simplified test bridge implementations.
Model Provider Test Removal
tests/functional_tests/models/gpt_oss/test_gpt_oss_provider.py, tests/unit_tests/models/*/test_*_provider.py
Deleted 8 provider test files covering DeepSeek, GLM, Kimi, Llama, Nemotron, and NemotronH variants (totaling 1000+ deleted test lines).
Functional Training Test Updates
tests/functional_tests/training/test_*.py (15+ files)
Replaced Llama-based provider usage (Llama32ModelProvider1B, Llama3ModelProvider variants) with GPTModelProvider across all training tests; added expanded configuration parameters.
Unit Test & Recipe Test Updates
tests/unit_tests/models/test_models_imports.py, tests/unit_tests/recipes/test_*.py, tests/unit_tests/recipes/nemotronh/test_*.py
Updated imports and isinstance checks to use new provider classes (MambaModelProvider, GPTModelProvider, MLAModelProvider); removed test imports for deleted provider classes; simplified monkeypatch strategies.
Conversion Test Helper & Config Tests
tests/unit_tests/training/test_config.py, tests/unit_tests/training/test_log_non_default_values.py
Updated DeepSeekModelProvider references to MLAModelProvider; adjusted expected transformer config parent classes.
Provider Bridge Type Hint Updates
src/megatron/bridge/models/conversion/model_bridge.py, src/megatron/bridge/models/llama_nemotron/llama_nemotron_bridge.py
Updated provider_bridge method return types from Llama-based providers to GPTModelProvider and LlamaNemotronHeterogeneousProvider respectively.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Modifications to example conversion comparison logic in compare.py align with logits normalization and truncation improvements.
  • Large-scale provider consolidation from legacy hierarchies (Llama, Nemotron, DeepSeek variants) to unified bridge-based architecture matches ongoing refactoring initiatives.
  • Test suite updates across functional and unit tests reflect the shift from provider-centric to bridge-centric model configuration patterns.

Suggested labels

refactoring, model-provider, bridge, consolidation, tests

Suggested reviewers

  • chtruong814
  • cuichenx
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a MiniMax-M2 MoE bridge to Megatron. It is concise, specific, and directly related to the core objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 82.08% which is sufficient. The required threshold is 80.00%.
Test Results For Major Changes ✅ Passed PR objectives document comprehensive test results including forward-pass cosine similarity (0.999990 for 1-GPU and EP=2) and weight round-trip verification across multiple parallelism settings, with functional tests and verification scripts included.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yuya/add-minimax-m2-bridge

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/unit_tests/recipes/test_run_plugins.py (1)

77-102: ⚠️ Potential issue | 🟡 Minor

Use the caller-provided sequence length in model config.

Line 77 accepts seq_length, but Line 101 hardcodes 8192. This ignores overrides and can desynchronize model vs dataset sequence lengths.

🔧 Minimal fix
-        seq_length=8192,
+        seq_length=seq_length,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit_tests/recipes/test_run_plugins.py` around lines 77 - 102, The
model config currently hardcodes seq_length=8192 in the GPTModelProvider call,
ignoring the caller-provided seq_length variable; update the GPTModelProvider
instantiation to use the seq_length variable (the one popped from kwargs at the
top of the function) instead of the literal 8192 so the model config matches the
dataset/training sequence length and respects overrides.
src/megatron/bridge/models/nemotron_vl/nemotron_vl_provider.py (1)

79-151: ⚠️ Potential issue | 🟠 Major

Do not silently ignore vp_stage in provide().

Line 79 accepts vp_stage, but the implementation never uses or validates it. That can silently construct the wrong model scope under virtual pipeline usage instead of failing fast.

🔒 Suggested guard
 def provide(self, pre_process=None, post_process=None, vp_stage=None):  # noqa: D401
     """Assemble a full :class:`~megatron.core.models.multimodal.llava_model.LLaVAModel`."""
+    if vp_stage is not None or getattr(self, "virtual_pipeline_model_parallel_size", None) is not None:
+        raise ValueError(
+            "Virtual pipeline parallelism is not supported in "
+            "NemotronNano12Bv2VLModelProvider.provide()."
+        )

     language_cfg = copy.deepcopy(self)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/megatron/bridge/models/nemotron_vl/nemotron_vl_provider.py` around lines
79 - 151, The provide() method accepts vp_stage but never uses or validates it,
which can silently construct an incorrect model under virtual pipeline; update
provide() (in nemotron_vl_provider.py) to validate vp_stage at the start (e.g.,
ensure it's None or within allowed stage range) and either pass it into the
LLaVAModel constructor if that class supports a virtual pipeline stage parameter
or raise a clear exception when an unsupported vp_stage is given; reference the
provide() function and the vp_stage parameter and ensure the guard runs before
assembling language_cfg/vision_cfg and creating the LLaVAModel so incorrect
scopes cannot be built.
🧹 Nitpick comments (10)
tests/unit_tests/recipes/test_moonlight_recipes.py (1)

18-18: Consider updating stale documentation and class naming.

The docstring on line 18 still references MoonlightModelProvider16B, and the fake class _FakeMoonlightModelProvider16B (line 76) is now used to mock MLAModelProvider. This inconsistency could confuse future maintainers.

✏️ Suggested documentation updates
 # Test purpose:
 # - Parametrize over all exported Moonlight recipe functions in `megatron.bridge.recipes.moonlight`.
-# - For each recipe, monkeypatch `MoonlightModelProvider16B` with a lightweight fake to avoid I/O.
+# - For each recipe, monkeypatch `MLAModelProvider` with a lightweight fake to avoid I/O.
 # - Build a config with small, safe overrides and assert it forms a valid `ConfigContainer`.
 # - Verify tokenizer selection and sanity-check parallelism fields.
-class _FakeMoonlightModelProvider16B:
-    """Fake MoonlightModelProvider16B for testing without model I/O."""
+class _FakeMLAModelProvider:
+    """Fake MLAModelProvider for testing Moonlight recipes without model I/O."""

If renaming the class, update all usages (lines 136, 171, 196, 222, 238, 261, 284, 307, 329).

Also applies to: 76-77

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit_tests/recipes/test_moonlight_recipes.py` at line 18, Update the
stale docstring and fake class name so they match the current provider type:
change any docstring text referencing MoonlightModelProvider16B to reference
MLAModelProvider (or the current provider name) and rename the fake class
_FakeMoonlightModelProvider16B to _FakeMLAModelProvider (or similar) and update
all monkeypatch usages that reference the old name (the test class and each
place where the fake is injected in the tests). Ensure the docstring, the fake
class declaration, and every usage/monkeypatch that currently points to
_FakeMoonlightModelProvider16B are updated to the new MLAModelProvider-based
names so they are consistent.
tests/unit_tests/training/test_config.py (1)

79-89: Consider renaming this function to reflect its current purpose.

The function is named create_test_deepseek_config but now creates an MLAModelProvider. Consider renaming to create_test_mla_config for consistency with the updated implementation. The test comment on line 1229 also references "Deepseek" but tests MLAModelProvider.

♻️ Suggested rename
-def create_test_deepseek_config(**kwargs: Any) -> MLAModelProvider:
-    """Creates an instance of MLAModelProvider for testing."""
+def create_test_mla_config(**kwargs: Any) -> MLAModelProvider:
+    """Creates an instance of MLAModelProvider for testing MLA-based models."""
     defaults = {
         "num_layers": 1,
         "hidden_size": 128,
         "num_attention_heads": 4,
         "seq_length": 512,
         "apply_rope_fusion": False,
     }
     defaults.update(kwargs)
     return MLAModelProvider(**defaults)

Also update the reference on line 1225 and comment on line 1229:

-    `@pytest.mark.parametrize`("model_factory", [create_test_gpt_config, create_test_deepseek_config])
+    `@pytest.mark.parametrize`("model_factory", [create_test_gpt_config, create_test_mla_config])
     def test_default_pipeline_dtype(self, model_factory, monkeypatch):
         """
-        Test pipeline_dtype is automatically set if None and PP enabled.
-        Test for both GPT and Deepseek to test both TransformerConfig types.
+        Test pipeline_dtype is automatically set if None and PP enabled.
+        Test for both GPT and MLA to test both TransformerConfig types.
         """
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit_tests/training/test_config.py` around lines 79 - 89, Rename the
helper function create_test_deepseek_config to create_test_mla_config and update
all call sites and comments that still reference "Deepseek" to instead reference
MLAModelProvider/MLA (e.g., change the function name and any test references and
the comment that mentions Deepseek to reflect MLAModelProvider), ensuring the
factory still returns MLAModelProvider(**defaults) and preserving the same
default kwargs.
tests/unit_tests/training/test_log_non_default_values.py (2)

58-71: Test logic is correct; consider renaming for clarity.

The test correctly verifies that MLAModelProvider returns MCoreMLATransformerConfig as its parent, and the docstring is accurate. The method name test_deepseek_provider_returns_mla_transformer_config could be renamed to test_mla_provider_returns_mla_transformer_config for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit_tests/training/test_log_non_default_values.py` around lines 58 -
71, Rename the test function to match the subject under test: change the
function name test_deepseek_provider_returns_mla_transformer_config to
test_mla_provider_returns_mla_transformer_config in the test file so it clearly
reflects MLAModelProvider; keep the existing docstring and assertions (including
the reference to _get_mcore_transformer_parent and MCoreMLATransformerConfig)
unchanged.

299-315: Test logic is correct; naming could be improved for consistency.

The test correctly verifies that MLAModelProvider uses MLATransformerConfig for comparison. However, the method name, docstring, and variable name still reference "DeepSeek" while testing the generic MLAModelProvider.

♻️ Suggested naming updates
     `@patch`("megatron.bridge.training.config.print_rank_0")
-    def test_handles_deepseek_model_correctly(self, mock_print_rank_0):
-        """Should use MLATransformerConfig for DeepSeek models."""
-        deepseek_model = MLAModelProvider(
+    def test_handles_mla_model_correctly(self, mock_print_rank_0):
+        """Should use MLATransformerConfig for MLA-based models."""
+        mla_model = MLAModelProvider(
             num_layers=2,
             hidden_size=128,
             num_attention_heads=4,
             seq_length=512,
         )

-        cfg = self._create_minimal_config_container(model_provider=deepseek_model)
+        cfg = self._create_minimal_config_container(model_provider=mla_model)

         cfg.log_non_default_values()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit_tests/training/test_log_non_default_values.py` around lines 299 -
315, Rename references to "DeepSeek" in the test to reflect the generic
MLAModelProvider: update the test method name
test_handles_deepseek_model_correctly, its docstring, and the variable
deepseek_model to names that reference MLAModelProvider (e.g.,
test_handles_mla_model_correctly and mla_model) so they match the actual class
under test (MLAModelProvider); keep the assertions and usage of
mock_print_rank_0, cfg, and cfg.log_non_default_values() unchanged and ensure
the assertion still verifies "MLATransformerConfig" appears in
mock_print_rank_0.call_args[0][0].
examples/conversion/compare_hf_and_megatron/compare.py (1)

612-613: Also disable grad_scale_func when turning off MTP.

For consistency with the existing MTP-disable pattern, set m.config.grad_scale_func = None alongside mtp_num_layers = None.

🔧 Suggested patch
     for m in megatron_model:
         m.config.mtp_num_layers = None
+        m.config.grad_scale_func = None

Based on learnings: In examples/conversion/compare_hf_and_megatron/compare.py, disabling MTP for inference used m.config.mtp_num_layers = None and m.config.grad_scale_func = None together.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/conversion/compare_hf_and_megatron/compare.py` around lines 612 -
613, When disabling MTP in the loop over megatron_model, also set the grad scale
function to None: inside the for m in megatron_model loop where you currently do
m.config.mtp_num_layers = None, add m.config.grad_scale_func = None so both
m.config.mtp_num_layers and m.config.grad_scale_func are cleared to disable MTP
consistently for inference.
src/megatron/bridge/recipes/moonlight/moonlight_16b.py (1)

199-255: Deduplicate shared MLAModelProvider kwargs between SFT and PEFT.

These two constructor blocks are large and mostly identical, which increases config drift risk.

♻️ Suggested refactor sketch
+def _moonlight_16b_model_kwargs(*, tp: int, ep: int, sequence_parallel: bool) -> dict:
+    return dict(
+        num_layers=27,
+        hidden_size=2048,
+        ffn_hidden_size=11264,
+        num_attention_heads=16,
+        kv_channels=16,
+        q_lora_rank=None,
+        kv_lora_rank=512,
+        num_moe_experts=64,
+        moe_ffn_hidden_size=1408,
+        moe_shared_expert_intermediate_size=2816,
+        moe_layer_freq=[0] * 1 + [1] * 26,
+        moe_router_topk=6,
+        moe_router_num_groups=1,
+        moe_router_group_topk=1,
+        moe_router_topk_scaling_factor=2.446,
+        moe_aux_loss_coeff=0.001,
+        make_vocab_size_divisible_by=1280,
+        moe_router_score_function="sigmoid",
+        moe_router_enable_expert_bias=True,
+        rotary_scaling_factor=1.0,
+        mscale=1.0,
+        mscale_all_dim=1.0,
+        rotary_base=50000,
+        layernorm_epsilon=1e-5,
+        init_method_std=0.02,
+        moe_router_bias_update_rate=1e-3,
+        rotary_percent=1.0,
+        vocab_size=163842,
+        normalization="RMSNorm",
+        activation_func=F.silu,
+        gated_linear_unit=True,
+        position_embedding_type="rope",
+        add_bias_linear=False,
+        share_embeddings_and_output_weights=False,
+        qk_layernorm=True,
+        bf16=True,
+        params_dtype=torch.bfloat16,
+        moe_grouped_gemm=True,
+        moe_token_dispatcher_type="alltoall",
+        tensor_model_parallel_size=tp,
+        pipeline_model_parallel_size=1,
+        pipeline_dtype=torch.bfloat16,
+        virtual_pipeline_model_parallel_size=None,
+        context_parallel_size=1,
+        expert_model_parallel_size=ep,
+        sequence_parallel=sequence_parallel,
+        expert_tensor_parallel_size=1,
+        recompute_granularity="selective",
+        recompute_modules=None,
+        recompute_method=None,
+        recompute_num_layers=None,
+    )
@@
-    cfg.model = MLAModelProvider(
-        ...
-    )
+    cfg.model = MLAModelProvider(**_moonlight_16b_model_kwargs(tp=2, ep=8, sequence_parallel=True))
@@
-    cfg.model = MLAModelProvider(
-        ...
-    )
+    cfg.model = MLAModelProvider(**_moonlight_16b_model_kwargs(tp=1, ep=2, sequence_parallel=False))

Also applies to: 413-468

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/megatron/bridge/recipes/moonlight/moonlight_16b.py` around lines 199 -
255, The MLAModelProvider constructor kwargs are duplicated between the SFT and
PEFT blocks (MLAModelProvider in moonlight_16b.py); extract the shared keyword
args into a single dict (e.g., common_model_kwargs or common_mla_config) and
pass it into both MLAModelProvider(...) calls (merge per-block overrides after
the shared dict), so SFT and PEFT only specify differences. Update any
references to parameters like num_layers, hidden_size, moe_*, rotary_*,
normalization, activation_func, bf16, params_dtype, parallelism and recompute_*
to live in the shared dict and leave only unique overrides in the individual
SFT/PEFT constructor calls.
tests/unit_tests/recipes/test_glm45_recipes.py (1)

138-153: Extract common monkeypatch setup into a pytest fixture.

This setup pattern is repeated and should be centralized to reduce drift across tests.

♻️ Example fixture extraction
+@pytest.fixture
+def patch_glm_recipe_io(monkeypatch: pytest.MonkeyPatch):
+    def _apply(mod, needs_tokenizer: bool):
+        monkeypatch.setattr(mod, "AutoBridge", _FakeBridge)
+        if needs_tokenizer:
+            import transformers
+            monkeypatch.setattr(
+                transformers,
+                "AutoTokenizer",
+                type("FakeAutoTokenizer", (), {"from_pretrained": staticmethod(lambda *args, **kwargs: _FakeTokenizer())}),
+            )
+    return _apply
@@
-def test_each_glm45_recipe_builds_config(recipe_func: Callable, monkeypatch: pytest.MonkeyPatch):
+def test_each_glm45_recipe_builds_config(recipe_func: Callable, patch_glm_recipe_io):
@@
-    monkeypatch.setattr(mod, "AutoBridge", _FakeBridge)
+    patch_glm_recipe_io(mod, is_sft_or_peft)

As per coding guidelines, "tests/**/*.py: Use pytest fixtures for common setup in unit tests."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit_tests/recipes/test_glm45_recipes.py` around lines 138 - 153,
Extract the repeated monkeypatch setup into a pytest fixture (e.g.,
fake_bridge_and_tokenizer) that accepts monkeypatch and recipe_func (or uses
request.param) and performs the same work: import the recipe module via
recipe_func.__module__, monkeypatch the module's AutoBridge to _FakeBridge,
compute is_sft_or_peft by checking "sft" or "peft" in
recipe_func.__name__.lower(), and when true monkeypatch
transformers.AutoTokenizer to a FakeAutoTokenizer that returns
_FakeTokenizer.from_pretrained; replace the inline code in tests with a use of
this fixture to centralize the AutoBridge/AutoTokenizer mocking and avoid
duplication.
examples/models/minimax_m2/inference.sh (1)

1-24: Add strict shell mode for safer multi-node execution.

Please add fail-fast shell options so partial failures do not continue silently.

♻️ Suggested hardening
 #!/usr/bin/env bash
+set -euo pipefail
+
 # Copyright (c) 2025, NVIDIA CORPORATION.  All rights reserved.
@@
-uv run python -m torch.distributed.run --nproc_per_node=8 \
+uv run --no-sync python -m torch.distributed.run --nproc_per_node=8 \

As per coding guidelines, "**/*.sh: Follow Google Shell Style Guide." Based on learnings, "prefer including --no-sync` when dependencies are already installed and environment/toolchain compatibility is known."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/models/minimax_m2/inference.sh` around lines 1 - 24, Add fail-fast
shell options and the recommended --no-sync flag: at the top of inference.sh
(right after the shebang) enable strict mode by adding "set -euo pipefail"
(optionally "set -x" for debug) so the script exits on any error/unset
variable/pipe failure, and update the distributed run command line (the "uv run
python -m torch.distributed.run --nproc_per_node=8 ...") to include the
"--no-sync" option when appropriate to avoid unnecessary dependency sync; ensure
any future shell variables in this file are quoted to remain safe under "set
-u".
src/megatron/bridge/recipes/nemotronh/nemotron_nano_v2.py (1)

37-77: Extract a shared Nano-v2 provider factory to reduce drift risk.

These six MambaModelProvider blocks duplicate a large set of identical fields. A small helper with variant-specific overrides would make updates safer and easier.

♻️ Refactor direction (example)
+def _build_nemotron_nano_v2_provider(
+    *,
+    variant: str,  # "9b" | "12b"
+    seq_length: int,
+    tensor_model_parallel_size: int,
+    sequence_parallel: bool,
+) -> MambaModelProvider:
+    base_kwargs = dict(
+        mamba_num_groups=8,
+        num_query_groups=8,
+        make_vocab_size_divisible_by=128,
+        activation_func=squared_relu,
+        masked_softmax_fusion=True,
+        apply_query_key_layer_scaling=False,
+        persist_layer_norm=True,
+        attention_softmax_in_fp32=False,
+        first_last_layers_bf16=True,
+        is_hybrid_model=True,
+        moe_aux_loss_coeff=0.0001,
+        moe_router_score_function="sigmoid",
+        moe_router_enable_expert_bias=True,
+        moe_router_load_balancing_type="seq_aux_loss",
+        moe_router_dtype="fp32",
+        moe_grouped_gemm=True,
+        moe_token_dispatcher_type="alltoall",
+        moe_permute_fusion=True,
+        moe_shared_expert_overlap=True,
+        pipeline_model_parallel_size=1,
+        pipeline_dtype=torch.bfloat16,
+        virtual_pipeline_model_parallel_size=None,
+        context_parallel_size=1,
+    )
+    variant_kwargs = {
+        "9b": dict(...),
+        "12b": dict(...),
+    }[variant]
+    return MambaModelProvider(
+        **base_kwargs,
+        **variant_kwargs,
+        seq_length=seq_length,
+        tensor_model_parallel_size=tensor_model_parallel_size,
+        sequence_parallel=sequence_parallel,
+    )

Also applies to: 172-212, 309-349, 435-475, 571-610, 718-757

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/megatron/bridge/recipes/nemotronh/nemotron_nano_v2.py` around lines 37 -
77, Multiple identical MambaModelProvider blocks (e.g., the cfg.model
assignment) should be consolidated into a shared factory to avoid drift; create
a helper function (e.g., make_nano_v2_provider or create_nano_v2_provider) that
returns a MambaModelProvider pre-populated with the common fields shown
(hybrid_override_pattern, num_layers, hidden_size, mamba_num_heads, kv_channels,
mamba_state_dim, ffn_hidden_size, num_attention_heads, mamba_head_dim,
seq_length, mamba_num_groups, num_query_groups, make_vocab_size_divisible_by,
activation_func, masked_softmax_fusion, apply_query_key_layer_scaling,
persist_layer_norm, attention_softmax_in_fp32, first_last_layers_bf16,
is_hybrid_model, moe_* settings, parallelism defaults, etc.), then replace each
duplicated MambaModelProvider instantiation (including cfg.model and the other
five blocks) with a call to this factory passing only variant-specific overrides
(e.g., tensor_model_parallel_size, pipeline_model_parallel_size, pipeline_dtype,
virtual_pipeline_model_parallel_size, context_parallel_size, sequence_parallel)
so updates to common settings are made in one place.
tests/functional_tests/training/test_decentralized_pg.py (1)

104-140: Extract a shared model-config factory to reduce maintenance drift across duplicated test configurations.

The same GPTModelProvider baseline is repeated across six test functions. A local helper with base kwargs and per-test overrides would eliminate drift-prone duplication and make test-specific variations clearer.

♻️ Refactor sketch
+    def _create_model_cfg(self, **overrides):
+        base = dict(
+            normalization="RMSNorm",
+            activation_func=F.silu,
+            gated_linear_unit=True,
+            position_embedding_type="rope",
+            add_bias_linear=False,
+            attention_dropout=0.0,
+            hidden_dropout=0.0,
+            bias_activation_fusion=True,
+            masked_softmax_fusion=True,
+            persist_layer_norm=True,
+            bias_dropout_fusion=True,
+            apply_rope_fusion=True,
+            num_query_groups=8,
+            init_method_std=0.02,
+            layernorm_epsilon=1e-05,
+            rotary_percent=1.0,
+            rope_scaling=True,
+            rope_scaling_factor=32.0,
+            rotary_base=500_000,
+            hidden_size=2048,
+            ffn_hidden_size=8192,
+            num_attention_heads=32,
+            attention_softmax_in_fp32=True,
+            pipeline_dtype=torch.bfloat16,
+            bf16=True,
+            make_vocab_size_divisible_by=128,
+            vocab_size=None,
+        )
+        base.update(overrides)
+        return GPTModelProvider(**base)

Applies to lines 104–140, 255–290, 410–446, 566–602, 722–758, 878–914.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/functional_tests/training/test_decentralized_pg.py` around lines 104 -
140, The repeated GPTModelProvider instantiation should be refactored into a
shared factory: create a local helper (e.g., make_gpt_model_cfg or
gpt_model_provider_factory) that constructs the base kwargs shown
(normalization, activation_func, gated_linear_unit, position_embedding_type,
etc.), accepts overrides for per-test changes, and returns a GPTModelProvider
instance; replace the six duplicated blocks that assign model_cfg with calls to
this helper, passing only test-specific overrides (like seq_length, num_layers,
share_embeddings_and_output_weights), so maintenance drift is eliminated and
variations remain explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/megatron/bridge/models/glm_vl/glm_45v_provider.py`:
- Around line 17-18: The file mixes old typing (Union/Optional/List) and unsafe
dataclass defaults: replace Union/Optional/List annotations with modern PEP
604/PEP 585 syntax (e.g., A | B, list[T], T | None) for the annotated variables
and function signatures (search for uses in e.g., any function or dataclass
fields around init_method_std and other annotated parameters), change the
annotation of init_method_std from int to float to match its float literal
default, and remove direct calls to functools.partial in dataclass defaults by
using dataclasses.field(default_factory=...) to construct the partial at
instance creation time (or convert to a module-level factory function) for
fields currently assigned partial(...) so you comply with RUF009 and
UP006/UP007.

In `@src/megatron/bridge/models/minimax_m2/minimax_m2_bridge.py`:
- Around line 27-31: The bridge currently registered with MegatronModelBridge
(decorator usage around MiniMaxM2ForCausalLM -> GPTModel) should be split to
follow repo conventions: move the orchestration/bridge implementation (the class
and register_bridge call) into a new model_bridge.py inside the model package,
and extract all parameter mapping logic into a new param_mapping.py; ensure
model_bridge.py imports the mapping functions/classes from param_mapping.py and
registers the bridge (same class name used now), and move the code currently
spanning the mapping section (previously around lines 95-149) into
param_mapping.py as exported mapping utilities referenced by model_bridge.py.
- Around line 73-82: The code currently silences unsupported QK-norm by setting
provider.qk_layernorm = False which drops q_norm.weight and k_norm.weight;
instead, make conversion fail-fast by detecting when those weights are present
and raising a NotImplementedError (or require an explicit opt-in flag) so users
cannot mistakenly get a misleading “successful” conversion. Update the logic
around provider.qk_layernorm in minimax_m2_bridge (and any conversion entrypoint
that calls it) to check for q_norm.weight/k_norm.weight in the source model and
either raise NotImplementedError with a descriptive message referencing
q_norm.weight and k_norm.weight and provider.qk_layernorm, or accept an explicit
parameter (e.g., allow_inexact_qk_norm) that must be true to proceed; ensure the
error message tells users how to opt in if desired.

In `@src/megatron/bridge/recipes/nemotronh/nemotron_3_nano.py`:
- Around line 211-259: The recipe passes raw strings to
MambaModelProvider.attention_backend but the provider expects the AttnBackend
enum; import AttnBackend and replace string usages (e.g.,
attention_backend="fused" and any later assignments like
cfg.model.attention_backend = "fused") with the corresponding AttnBackend enum
member (for example AttnBackend.fused or AttnBackend.auto as appropriate) so all
occurrences in this recipe (including the MambaModelProvider constructor and
subsequent cfg.model.attention_backend assignments) use the enum type instead of
raw strings.

In `@src/megatron/bridge/recipes/nemotronh/nemotronh.py`:
- Around line 443-456: The NemotronH 56B configs create MambaModelProvider with
attention_backend=AttnBackend.auto but later assignments overwrite it to None;
update the code so the constructor parameter is authoritative by removing the
subsequent assignments that set attention_backend = None (or, if auto is
intended later, replace those None assignments with AttnBackend.auto) in each
config block where cfg.model = MambaModelProvider(...) appears (e.g., the block
starting with cfg.model = MambaModelProvider and the later places noted around
the other config functions); ensure only one definitive assignment to
attention_backend remains so AttnBackend.auto is not silently discarded.

In `@tests/functional_tests/models/minimax_m2/test_minimax_m2_conversion.py`:
- Around line 169-173: Replace the fragile "assert False" in the failure path
with an explicit exception so failures are raised even under Python -O;
specifically, where you check result.returncode != 0 (using variables result and
test_name), keep the STDOUT/STDERR prints but replace the assert False line with
a raise (e.g., raise RuntimeError or AssertionError) that includes the same
message and return code information so the test fails reliably and provides
context.
- Around line 15-17: The test uses hardcoded interpreter and absolute repo paths
and an bare assert; update the subprocess invocation(s) in
test_minimax_m2_conversion.py to use sys.executable instead of "python", compute
repository paths relative to the test file using
Path(__file__).resolve().parents[...] (replace occurrences of
"/opt/Megatron-Bridge/" with the computed path) and pass cwd to subprocess.run,
use capture_output=True/text=True and check=True for portable behavior, and wrap
the subprocess call in a try/except catching subprocess.CalledProcessError (or
Exception) and call pytest.fail(...) with the captured stdout/stderr on error
instead of using assert False. Ensure you update imports to include sys and
pytest and adjust the function or block that performs the conversion accordingly
(identify the subprocess.run call and its surrounding error handling).

In `@tests/unit_tests/models/glm_vl/test_glm_45v_provider.py`:
- Around line 240-243: The test class TestGLM45VModelProviderInheritance is
missing a pytest category marker; add the `@pytest.mark.unit` decorator above the
class definition to categorize it as a unit test and ensure pytest is imported
(add "import pytest" at the top if not already present). Locate the class named
TestGLM45VModelProviderInheritance and prepend the decorator line
`@pytest.mark.unit` directly above the class declaration so the test follows the
repository's pytest.mark conventions.

---

Outside diff comments:
In `@src/megatron/bridge/models/nemotron_vl/nemotron_vl_provider.py`:
- Around line 79-151: The provide() method accepts vp_stage but never uses or
validates it, which can silently construct an incorrect model under virtual
pipeline; update provide() (in nemotron_vl_provider.py) to validate vp_stage at
the start (e.g., ensure it's None or within allowed stage range) and either pass
it into the LLaVAModel constructor if that class supports a virtual pipeline
stage parameter or raise a clear exception when an unsupported vp_stage is
given; reference the provide() function and the vp_stage parameter and ensure
the guard runs before assembling language_cfg/vision_cfg and creating the
LLaVAModel so incorrect scopes cannot be built.

In `@tests/unit_tests/recipes/test_run_plugins.py`:
- Around line 77-102: The model config currently hardcodes seq_length=8192 in
the GPTModelProvider call, ignoring the caller-provided seq_length variable;
update the GPTModelProvider instantiation to use the seq_length variable (the
one popped from kwargs at the top of the function) instead of the literal 8192
so the model config matches the dataset/training sequence length and respects
overrides.

---

Nitpick comments:
In `@examples/conversion/compare_hf_and_megatron/compare.py`:
- Around line 612-613: When disabling MTP in the loop over megatron_model, also
set the grad scale function to None: inside the for m in megatron_model loop
where you currently do m.config.mtp_num_layers = None, add
m.config.grad_scale_func = None so both m.config.mtp_num_layers and
m.config.grad_scale_func are cleared to disable MTP consistently for inference.

In `@examples/models/minimax_m2/inference.sh`:
- Around line 1-24: Add fail-fast shell options and the recommended --no-sync
flag: at the top of inference.sh (right after the shebang) enable strict mode by
adding "set -euo pipefail" (optionally "set -x" for debug) so the script exits
on any error/unset variable/pipe failure, and update the distributed run command
line (the "uv run python -m torch.distributed.run --nproc_per_node=8 ...") to
include the "--no-sync" option when appropriate to avoid unnecessary dependency
sync; ensure any future shell variables in this file are quoted to remain safe
under "set -u".

In `@src/megatron/bridge/recipes/moonlight/moonlight_16b.py`:
- Around line 199-255: The MLAModelProvider constructor kwargs are duplicated
between the SFT and PEFT blocks (MLAModelProvider in moonlight_16b.py); extract
the shared keyword args into a single dict (e.g., common_model_kwargs or
common_mla_config) and pass it into both MLAModelProvider(...) calls (merge
per-block overrides after the shared dict), so SFT and PEFT only specify
differences. Update any references to parameters like num_layers, hidden_size,
moe_*, rotary_*, normalization, activation_func, bf16, params_dtype, parallelism
and recompute_* to live in the shared dict and leave only unique overrides in
the individual SFT/PEFT constructor calls.

In `@src/megatron/bridge/recipes/nemotronh/nemotron_nano_v2.py`:
- Around line 37-77: Multiple identical MambaModelProvider blocks (e.g., the
cfg.model assignment) should be consolidated into a shared factory to avoid
drift; create a helper function (e.g., make_nano_v2_provider or
create_nano_v2_provider) that returns a MambaModelProvider pre-populated with
the common fields shown (hybrid_override_pattern, num_layers, hidden_size,
mamba_num_heads, kv_channels, mamba_state_dim, ffn_hidden_size,
num_attention_heads, mamba_head_dim, seq_length, mamba_num_groups,
num_query_groups, make_vocab_size_divisible_by, activation_func,
masked_softmax_fusion, apply_query_key_layer_scaling, persist_layer_norm,
attention_softmax_in_fp32, first_last_layers_bf16, is_hybrid_model, moe_*
settings, parallelism defaults, etc.), then replace each duplicated
MambaModelProvider instantiation (including cfg.model and the other five blocks)
with a call to this factory passing only variant-specific overrides (e.g.,
tensor_model_parallel_size, pipeline_model_parallel_size, pipeline_dtype,
virtual_pipeline_model_parallel_size, context_parallel_size, sequence_parallel)
so updates to common settings are made in one place.

In `@tests/functional_tests/training/test_decentralized_pg.py`:
- Around line 104-140: The repeated GPTModelProvider instantiation should be
refactored into a shared factory: create a local helper (e.g.,
make_gpt_model_cfg or gpt_model_provider_factory) that constructs the base
kwargs shown (normalization, activation_func, gated_linear_unit,
position_embedding_type, etc.), accepts overrides for per-test changes, and
returns a GPTModelProvider instance; replace the six duplicated blocks that
assign model_cfg with calls to this helper, passing only test-specific overrides
(like seq_length, num_layers, share_embeddings_and_output_weights), so
maintenance drift is eliminated and variations remain explicit.

In `@tests/unit_tests/recipes/test_glm45_recipes.py`:
- Around line 138-153: Extract the repeated monkeypatch setup into a pytest
fixture (e.g., fake_bridge_and_tokenizer) that accepts monkeypatch and
recipe_func (or uses request.param) and performs the same work: import the
recipe module via recipe_func.__module__, monkeypatch the module's AutoBridge to
_FakeBridge, compute is_sft_or_peft by checking "sft" or "peft" in
recipe_func.__name__.lower(), and when true monkeypatch
transformers.AutoTokenizer to a FakeAutoTokenizer that returns
_FakeTokenizer.from_pretrained; replace the inline code in tests with a use of
this fixture to centralize the AutoBridge/AutoTokenizer mocking and avoid
duplication.

In `@tests/unit_tests/recipes/test_moonlight_recipes.py`:
- Line 18: Update the stale docstring and fake class name so they match the
current provider type: change any docstring text referencing
MoonlightModelProvider16B to reference MLAModelProvider (or the current provider
name) and rename the fake class _FakeMoonlightModelProvider16B to
_FakeMLAModelProvider (or similar) and update all monkeypatch usages that
reference the old name (the test class and each place where the fake is injected
in the tests). Ensure the docstring, the fake class declaration, and every
usage/monkeypatch that currently points to _FakeMoonlightModelProvider16B are
updated to the new MLAModelProvider-based names so they are consistent.

In `@tests/unit_tests/training/test_config.py`:
- Around line 79-89: Rename the helper function create_test_deepseek_config to
create_test_mla_config and update all call sites and comments that still
reference "Deepseek" to instead reference MLAModelProvider/MLA (e.g., change the
function name and any test references and the comment that mentions Deepseek to
reflect MLAModelProvider), ensuring the factory still returns
MLAModelProvider(**defaults) and preserving the same default kwargs.

In `@tests/unit_tests/training/test_log_non_default_values.py`:
- Around line 58-71: Rename the test function to match the subject under test:
change the function name test_deepseek_provider_returns_mla_transformer_config
to test_mla_provider_returns_mla_transformer_config in the test file so it
clearly reflects MLAModelProvider; keep the existing docstring and assertions
(including the reference to _get_mcore_transformer_parent and
MCoreMLATransformerConfig) unchanged.
- Around line 299-315: Rename references to "DeepSeek" in the test to reflect
the generic MLAModelProvider: update the test method name
test_handles_deepseek_model_correctly, its docstring, and the variable
deepseek_model to names that reference MLAModelProvider (e.g.,
test_handles_mla_model_correctly and mla_model) so they match the actual class
under test (MLAModelProvider); keep the assertions and usage of
mock_print_rank_0, cfg, and cfg.log_non_default_values() unchanged and ensure
the assertion still verifies "MLATransformerConfig" appears in
mock_print_rank_0.call_args[0][0].

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 383b610 and 1434650.

📒 Files selected for processing (70)
  • examples/conversion/compare_hf_and_megatron/compare.py
  • examples/models/minimax_m2/conversion.sh
  • examples/models/minimax_m2/inference.sh
  • examples/models/minimax_m2/verify_toy_model.py
  • src/megatron/bridge/models/__init__.py
  • src/megatron/bridge/models/conversion/model_bridge.py
  • src/megatron/bridge/models/deepseek/__init__.py
  • src/megatron/bridge/models/deepseek/deepseek_provider.py
  • src/megatron/bridge/models/glm/__init__.py
  • src/megatron/bridge/models/glm/glm45_provider.py
  • src/megatron/bridge/models/glm_vl/glm_45v_provider.py
  • src/megatron/bridge/models/gpt_oss/__init__.py
  • src/megatron/bridge/models/gpt_oss/gpt_oss_provider.py
  • src/megatron/bridge/models/hf_pretrained/utils.py
  • src/megatron/bridge/models/kimi/__init__.py
  • src/megatron/bridge/models/kimi/kimi_provider.py
  • src/megatron/bridge/models/llama/__init__.py
  • src/megatron/bridge/models/llama/llama_provider.py
  • src/megatron/bridge/models/llama_nemotron/__init__.py
  • src/megatron/bridge/models/llama_nemotron/llama_nemotron_bridge.py
  • src/megatron/bridge/models/llama_nemotron/llama_nemotron_provider.py
  • src/megatron/bridge/models/minimax_m2/__init__.py
  • src/megatron/bridge/models/minimax_m2/minimax_m2_bridge.py
  • src/megatron/bridge/models/nemotron/__init__.py
  • src/megatron/bridge/models/nemotron/nemotron_provider.py
  • src/megatron/bridge/models/nemotron_vl/__init__.py
  • src/megatron/bridge/models/nemotron_vl/nemotron_vl_bridge.py
  • src/megatron/bridge/models/nemotron_vl/nemotron_vl_provider.py
  • src/megatron/bridge/models/nemotronh/__init__.py
  • src/megatron/bridge/models/nemotronh/nemotron_h_provider.py
  • src/megatron/bridge/recipes/kimi/kimi_k2.py
  • src/megatron/bridge/recipes/moonlight/moonlight_16b.py
  • src/megatron/bridge/recipes/nemotronh/nemotron_3_nano.py
  • src/megatron/bridge/recipes/nemotronh/nemotron_nano_v2.py
  • src/megatron/bridge/recipes/nemotronh/nemotronh.py
  • tests/functional_tests/data/test_samplers.py
  • tests/functional_tests/models/gpt_oss/test_gpt_oss_provider.py
  • tests/functional_tests/models/minimax_m2/__init__.py
  • tests/functional_tests/models/minimax_m2/test_minimax_m2_conversion.py
  • tests/functional_tests/training/test_callbacks.py
  • tests/functional_tests/training/test_decentralized_pg.py
  • tests/functional_tests/training/test_finetune_dora.py
  • tests/functional_tests/training/test_finetune_lora.py
  • tests/functional_tests/training/test_inprocess_restart.py
  • tests/functional_tests/training/test_megatron_fsdp.py
  • tests/functional_tests/training/test_nvrx_straggler.py
  • tests/functional_tests/training/test_pretrain.py
  • tests/functional_tests/training/test_pretrain_resume.py
  • tests/functional_tests/training/test_sample_based_training.py
  • tests/functional_tests/training/test_sft.py
  • tests/functional_tests/training/test_tensor_inspect.py
  • tests/unit_tests/models/deepseek/test_deepseek_provider.py
  • tests/unit_tests/models/glm/test_glm45_provider.py
  • tests/unit_tests/models/glm_vl/test_glm_45v_provider.py
  • tests/unit_tests/models/gpt_oss/test_gpt_oss_provider.py
  • tests/unit_tests/models/kimi/test_kimi_provider.py
  • tests/unit_tests/models/llama/test_llama_provider.py
  • tests/unit_tests/models/llama_nemotron/test_llama_nemotron_bridge.py
  • tests/unit_tests/models/nemotron/test_nemotron_provider.py
  • tests/unit_tests/models/nemotronh/test_nemotron_h_provider.py
  • tests/unit_tests/models/test_models_imports.py
  • tests/unit_tests/recipes/kimi/test_kimi_k2.py
  • tests/unit_tests/recipes/nemotronh/test_nemotron_3_nano.py
  • tests/unit_tests/recipes/nemotronh/test_nemotron_nano_v2.py
  • tests/unit_tests/recipes/nemotronh/test_nemotronh.py
  • tests/unit_tests/recipes/test_glm45_recipes.py
  • tests/unit_tests/recipes/test_moonlight_recipes.py
  • tests/unit_tests/recipes/test_run_plugins.py
  • tests/unit_tests/training/test_config.py
  • tests/unit_tests/training/test_log_non_default_values.py
💤 Files with no reviewable changes (22)
  • src/megatron/bridge/models/glm/glm45_provider.py
  • tests/unit_tests/models/llama_nemotron/test_llama_nemotron_bridge.py
  • tests/unit_tests/models/llama/test_llama_provider.py
  • tests/unit_tests/models/kimi/test_kimi_provider.py
  • src/megatron/bridge/models/nemotron/init.py
  • src/megatron/bridge/models/kimi/kimi_provider.py
  • tests/unit_tests/models/deepseek/test_deepseek_provider.py
  • src/megatron/bridge/models/gpt_oss/gpt_oss_provider.py
  • src/megatron/bridge/models/llama_nemotron/init.py
  • tests/unit_tests/models/nemotronh/test_nemotron_h_provider.py
  • src/megatron/bridge/models/llama/llama_provider.py
  • src/megatron/bridge/models/deepseek/deepseek_provider.py
  • tests/functional_tests/models/gpt_oss/test_gpt_oss_provider.py
  • src/megatron/bridge/models/glm/init.py
  • tests/unit_tests/models/glm/test_glm45_provider.py
  • src/megatron/bridge/models/nemotronh/init.py
  • src/megatron/bridge/models/nemotron_vl/init.py
  • src/megatron/bridge/models/gpt_oss/init.py
  • src/megatron/bridge/models/nemotronh/nemotron_h_provider.py
  • src/megatron/bridge/models/nemotron/nemotron_provider.py
  • tests/unit_tests/models/gpt_oss/test_gpt_oss_provider.py
  • tests/unit_tests/models/nemotron/test_nemotron_provider.py

Comment thread src/megatron/bridge/models/glm_vl/glm_45v_provider.py
Comment thread src/megatron/bridge/models/minimax_m2/minimax_m2_bridge.py
Comment thread src/megatron/bridge/models/minimax_m2/minimax_m2_bridge.py Outdated
Comment thread src/megatron/bridge/recipes/nemotronh/nemotron_3_nano.py
Comment thread src/megatron/bridge/recipes/nemotronh/nemotronh.py
Comment thread tests/unit_tests/models/glm_vl/test_glm_45v_provider.py
@yaoyu-33 yaoyu-33 mentioned this pull request Mar 4, 2026
9 tasks
yaoyu-33 added 6 commits March 6, 2026 09:31
Introduce FusedExpertMapping and FusedGatedExpertMapping in
param_mapping.py to handle many-to-one / one-to-many expert weight
conversions generically. This eliminates duplicated
maybe_modify_converted_hf_weight overrides and hf_weights_cache from
GPT-OSS, GLM-4.5, GLM-4.5V, and Qwen3-VL bridges (-502 / +307 lines).

Also fixes two pre-existing bugs:
- GLM-4.5 MTP mappings used stale 'transformer_layer' instead of
  'mtp_model_layer', causing missing-mapping warnings
- hf_to_megatron_generate_text.py set mtp_num_layers=None which crashed
  MTP-enabled models; replaced with m.mtp_process=False

Signed-off-by: Yu Yao <yaoyu.094@gmail.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
- Remove NemotronNano12Bv2Provider from nemotron_vl/__init__.py
  (was a deprecated alias from deleted nemotron_h_provider.py)
- Remove invalid max_position_embeddings kwarg from kimi and moonlight
  recipes (not a field on MLAModelProvider)
- Update moonlight test to monkeypatch MLAModelProvider instead of
  deleted MoonlightModelProvider16B

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
Add Megatron Bridge for MiniMaxAI/MiniMax-M2, a sparse MoE model with
256 experts, top-8 sigmoid routing, and expert bias correction.

Includes:
- Bridge with config mapping and per-expert weight conversion
  (block_sparse_moe prefix, w1/w2/w3 format)
- Partial RoPE support (rotary_dim -> rotary_percent)
- QK layernorm intentionally disabled (full-dim vs per-head mismatch)
- Functional test with toy model for TP/PP/EP parallelism
- Example scripts for conversion, inference, and verification
- compare.py fix: truncate Megatron logits to HF vocab size for
  proper comparison when Megatron pads vocab for kernel efficiency

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
…ti-node support for MiniMax-M2

Add custom full-dimension QK normalization (minimax_m2_provider.py) since
MiniMax-M2 applies RMSNorm over the entire Q/K projection rather than
per-head. The implementation uses sum-of-squares all-reduce across TP
ranks and provides sharded_state_dict for distributed checkpointing.

Add on-the-fly FP8 block-wise dequantization in the bridge via
maybe_modify_loaded_hf_weight, converting float8_e4m3fn weights to
bfloat16 using per-block scale_inv factors during HF->Megatron
conversion.

Add multi-node Slurm scripts (slurm_conversion.sh, slurm_inference.sh)
for configurations requiring TP*EP*PP > 8 GPUs.

Update verify_toy_model.py to extract real pretrained weights (N layers)
from the FP8 model, dequantize to bf16, and verify round-trip accuracy.

Fix dtype mismatch handling in hf_megatron_roundtrip_multi_gpu.py for
FP8 source models.

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
…x-M2 expert mappings

- Add missing FusedGatedExpertMapping alias (GLMExpertGateUpProjMapping)
  to glm_moe_mappings.py, fixing ImportError after fused expert refactor
- Remove duplicate local_experts.* mappings from MiniMax-M2 bridge since
  moe_grouped_gemm=True (only grouped-gemm weight* path needed)

Verified: TP=2, PP=2, EP=2 roundtrip tests pass on cluster with zero
mapping warnings.

Signed-off-by: Yu Yao <yaoyu.094@gmail.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
@yaoyu-33
yaoyu-33 force-pushed the yuya/add-minimax-m2-bridge branch from b130369 to a86dc26 Compare March 6, 2026 17:30
@yaoyu-33

yaoyu-33 commented Mar 6, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test a86dc26

yaoyu-33 added 2 commits March 6, 2026 10:50
Remove verify_toy_model.py dev script. Align conversion.sh and
inference.sh with GPT-OSS pattern (import + export + roundtrip,
multi-checkpoint inference). Rewrite slurm_conversion.sh to sweep
parallelism configs (TP,PP,EP) with roundtrip validation. Clean up
slurm_inference.sh for consistency.

All configs verified on cluster-cw with toy model:
  TP=2,PP=1,EP=4 | TP=1,PP=2,EP=4 | TP=2,PP=2,EP=2 → EXIT=0

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
…eanup

- Add SLURM env var auto-population in model_provider.py for srun launches
  (RANK, WORLD_SIZE, LOCAL_RANK, MASTER_ADDR, MASTER_PORT from SLURM vars)
- Increase NCCL init_process_group timeout to 60 minutes for large MoE models
- Fix ImportError crash in save_artifacts for trust_remote_code models
- Accept SLURM_NTASKS in hf_megatron_roundtrip_multi_gpu.py for srun launches
- Rewrite MiniMax-M2 slurm scripts to use srun-native (ntasks-per-node=8)
  instead of torch.distributed.run
- Remove single-node conversion.sh/inference.sh (MiniMax-M2 requires multi-node)
- Set verified parallelism defaults: TP=2,EP=8 roundtrip; TP=1,EP=16 inference

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
@yaoyu-33

yaoyu-33 commented Mar 7, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 57363f0

…or PR #2628)

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Made-with: Cursor
@yaoyu-33

yaoyu-33 commented Mar 7, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 206c4fb

yaoyu-33 and others added 7 commits March 7, 2026 08:51
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
…mappings

The refactor in param_mapping.py renamed GLMExpertGateUpProjMapping to
FusedGatedExpertMapping but only added GLMExpertDownProjMapping alias
in glm_moe_mappings.py. Add the missing alias so existing bridge imports
(glm45_bridge.py, glm_45v_bridge.py) continue to work.

Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Split multi-name import block into two separate import statements,
each with per-line # noqa: F401 comments, to satisfy ruff's import
block formatting requirements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
…ext tests

- Set PROVIDER_CLASS = Qwen3NextModelProvider so super().provider_bridge()
  instantiates the correct provider (not GPTModelProvider which lacks
  MLA/hybrid fields like q_lora_rank)
- Add value is not None guard in hf_config_to_provider_kwargs to skip
  None-valued config fields
- Add null_attr fixture loop in test mocks to suppress Mock() objects
  for MLA/alternative-expert CONFIG_MAPPING fields

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Keep once-per-class dtype mismatch warning from HEAD (suppresses duplicate
warnings) over main's per-call version.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
- Remove dtype-mismatch silencing in hf_megatron_roundtrip_multi_gpu.py:
  only cast to float32 for params listed in IGNORE_PRECISION_PARAMS,
  not silently whenever dtypes differ (which would hide real issues)
- Remove /lustre reference from MiniMax-M2 slurm_conversion.sh example mount

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test 2c692fe

…atrix

Move minimax_m2 functional tests to test_groups/models/minimax_m2/ to match
the reorganized structure introduced in main, add active launch script under
launch_scripts/active/, and use the generate-test-matrix dynamic matrix
instead of the static L0 matrix from this branch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test 02d739b

HF transformers stores the MoE block as `mlp` (not `block_sparse_moe`)
and expert weights as stacked tensors (`mlp.experts.gate_up_proj`,
`mlp.experts.down_proj`) rather than per-expert w1/w2/w3 params.

- Replace `block_sparse_moe.gate.weight` -> `mlp.gate.weight`
- Replace `block_sparse_moe.e_score_correction_bias` -> `mlp.e_score_correction_bias`
- Replace `GatedMLPMapping` (per-expert) with `FusedGatedExpertMapping`
  for stacked `gate_up_proj` [num_experts, 2*intermediate, hidden]
- Replace per-expert `AutoMapping` with `FusedExpertMapping`
  for stacked `down_proj` [num_experts, hidden, intermediate]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Mar 23, 2026

Copy link
Copy Markdown

/ok to test

@yaoyu-33, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

The MiniMaxAI/MiniMax-M2 checkpoint uses the pre-merge custom code layout
(block_sparse_moe prefix, per-expert w1/w3/w2 weights), while a natively
constructed transformers >= 5.0 model uses a different layout (mlp prefix,
stacked gate_up_proj/down_proj tensors). The CI test creates a toy model
with the native transformers format, causing the bridge (written for the
legacy format) to fail.

Fix: auto-detect the checkpoint format in build_conversion_tasks() by
inspecting the HF state dict keys, then select the appropriate param
mapping in mapping_registry() — native (FusedGatedExpertMapping +
FusedExpertMapping) or legacy (GatedMLPMapping + AutoMapping per-expert).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Mar 23, 2026

Copy link
Copy Markdown

/ok to test

@yaoyu-33, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

…ayout

The original bridge was written against the MiniMaxAI/MiniMax-M2 HF
checkpoint's custom code (block_sparse_moe prefix, per-expert w1/w3/w2),
which is incompatible with the native transformers >= 5.0 implementation
(mlp prefix, stacked gate_up_proj/down_proj).

Switch exclusively to the native transformers layout:
- router/bias: mlp.gate.weight / mlp.e_score_correction_bias
- experts: FusedGatedExpertMapping + FusedExpertMapping for stacked tensors
- Remove format auto-detection logic
- Drop --trust-remote-code from slurm_inference.sh (native impl required)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Mar 23, 2026

Copy link
Copy Markdown

/ok to test

@yaoyu-33, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

…ridge

transformers 5.x serializes MiniMax-M2 with the legacy block_sparse_moe
prefix (per-expert w1/w3/w2 weights) even though the in-memory model API
uses mlp/gate_up_proj/down_proj. Switch from FusedGatedExpertMapping /
FusedExpertMapping (which expect a stacked native tensor) to GatedMLPMapping
/ AutoMapping with per-expert wildcard patterns, matching the on-disk format
of both the HF hub checkpoint and any save_pretrained output.

Also fix parents[4] → parents[5] in the functional test so repo_root
resolves to the project root instead of the tests/ subdirectory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Mar 23, 2026

Copy link
Copy Markdown

/ok to test

@yaoyu-33, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test 53f2d66

Use hardcoded /opt/Megatron-Bridge paths for --data-file and --source,
matching the pattern used by the qwen3_moe functional test. The dynamic
tmp_path-based coverage file caused issues when sys.executable resolves
to the container Python rather than the uv-managed venv.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: yaoyu-33 <yaoyu.094@gmail.com>
@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test

@copy-pr-bot

copy-pr-bot Bot commented Mar 23, 2026

Copy link
Copy Markdown

/ok to test

@yaoyu-33, there was an error processing your request: E1

See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/

@yaoyu-33

Copy link
Copy Markdown
Contributor Author

/ok to test c1bfdf2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants